﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Configuration;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Xml.Linq;
using CandyBlast;
using Candy.Web.Data;
using Candy.Web.Model;
using System.Net;



namespace Candy.Web
{
    [EnableClientAccess()]
    public class CandyDataService : DomainService
    {
        private string connectionStrCandyMaster = global::System.Configuration.ConfigurationManager.ConnectionStrings["CandyMaster"].ConnectionString;

        private string CurrentUserName
        {
            //return HttpContext.Current.Profile.UserName;
            get
            {

                return ServiceContext.User.Identity.Name;
            }
        }

        private AuthUser CurrentUser
        {
            //return HttpContext.Current.Profile.UserName;
            get
            {

                using (CandyAuth auth = new CandyAuth())
                {
                    AuthUser ret = auth.GetAuthenticatedUserInternal(ServiceContext.User);
                    return ret;
                }
            }
        }

        #region ProjectManagement
        [RequiresAuthentication()]
        [Query]
        public IQueryable<ProjectDto> GetProjects()
        {
            Version minVerison = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
            List<ProjectDto> ret = new List<ProjectDto>();
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                var v = context.Projects.ToList().Select(x => Version.Parse(x.CandyVersion)).ToList();
                foreach (var item in context.Projects.ToList().Where(x => Version.Parse(x.CandyVersion) >= minVerison))
                {
                    try
                    {
                        ProjectDto p =
                      new ProjectDto
                            {
                                Id = item.Id,
                                Name = item.Name,
                                Description = item.Description,
                                Owner = CandySecurityManager.GetOwner(item.Id).FirstOrDefault()
                            };

                        using (CandyDbDataContext projectContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(item.Id)))
                        {
                            bool importAllValue = false;
                            var importAll = projectContext.Settings.FirstOrDefault(x => x.Name == "AllowImportAll");
                            if (importAll != null)
                            {

                                bool.TryParse(importAll.Value, out importAllValue);
                            }
                            p.AllowImportAll = importAllValue;
                        }
                        ret.Add(p);
                    }
                    catch
                    {
                        //TODO: project dab offline or deleted ??
                    }
                }

            }
            return ret.AsQueryable();
        }

        [Insert]
        [RequiresAuthentication]
        public void InsertProject(ProjectDto project)
        {

            if (ProjectExists(project.Name))
            {
                ValidationResult error =

       new ValidationResult(Resources.ValidationErrorResource.ValidationProjectNameAlreadyExists,
                            new string[] { "Name" });

                throw new ValidationException(error, null, project);
            }

            Version candyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
            int projectId = DatabaseManager.CreateProjectDB(project, candyVersion, CurrentUser.UserID, CurrentUser.Name);
            project.Id = projectId;


            CandySecurityManager.CreateProjectRoles(project);
            CandySecurityManager.AddToOwners(CurrentUserName, project.Id);
            project.Owner = CandySecurityManager.GetOwner(projectId).FirstOrDefault();
        }


        [Update]
        [RequiresAuthentication]
        public void UpdateProject(ProjectDto projectDto)
        {
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                var project = context.Projects.Single(x => x.Id == projectDto.Id);
                project.Description = projectDto.Description;
                project.IsPublic = projectDto.IsPublic;
                context.SubmitChanges();
            }
            using (CandyDbDataContext projectContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectDto.Id)))
            {
                var importAll = projectContext.Settings.FirstOrDefault(x => x.Name == "AllowImportAll");
                if (importAll != null)
                {
                    importAll.Value = projectDto.AllowImportAll.ToString();
                }
                else
                {
                    Setting s = new Setting()
                    {
                        Name = "AllowImportAll",
                        Value = projectDto.AllowImportAll.ToString()
                    };
                    projectContext.Settings.InsertOnSubmit(s);
                }
                projectContext.SubmitChanges();
            }
        }


        [Delete]
        public void DeleteProject(ProjectDto project)
        {
            //TODO:only allow the owner or SU the project to delete the project
            //TODO:delete
            //  CandySecurityManager.DeleteProjectRoles(project);
        }

        [Query]
        public IQueryable<MembershipRequestDto> GetMembershipRequestsByProject(int projectId)
        {
            List<MembershipRequestDto> ret = new List<MembershipRequestDto>();
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                var requests = context.MembershipRequests.Where(x => x.ProjectId == projectId).Select(x => new MembershipRequestDto
                 {
                     Date = x.DateTime,
                     ProjectId = x.ProjectId,
                     Message = x.Message,
                     RoleType = (RoleType)Enum.Parse(typeof(RoleType), x.Role),
                     User = x.aspnet_User.UserName,
                     UserUId = x.UserUid
                 });
                ret = requests.ToList();
            }

            return ret.AsQueryable();
        }

        [Query]
        public IQueryable<MembershipRequestDto> GetMyMembershipRequests()
        {
            List<MembershipRequestDto> ret = new List<MembershipRequestDto>();
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                var requests = context.MembershipRequests.Where(x => x.UserUid == CurrentUser.UserID).Select(x => new MembershipRequestDto
                {
                    Date = x.DateTime,
                    ProjectId = x.ProjectId,
                    Message = x.Message,
                    RoleType = (RoleType)Enum.Parse(typeof(RoleType), x.Role),
                    User = x.aspnet_User.UserName,
                    UserUId = x.UserUid
                });
                ret = requests.ToList();
            }

            return ret.AsQueryable();
        }



        [Invoke(HasSideEffects = false)]
        [RequiresAuthentication]
        public void RequestMembership(int projectId, RoleType role, string message)
        {

            AuthUser me = CurrentUser;

            string ownerName = "";
            string project = "";
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                ownerName = context.UserRoleViews.Where(x => x.RoleName == RoleDefinition.FormatOwnerRole(projectId)).SingleOrDefault().UserName;
                project = context.Projects.Single(x => x.Id == projectId).Name;
                MembershipRequest mr = context.MembershipRequests.SingleOrDefault(x => x.UserUid == me.UserID && x.ProjectId == projectId);
                if (mr == null)
                {
                    mr = new MembershipRequest { UserUid = me.UserID, ProjectId = projectId };
                    context.MembershipRequests.InsertOnSubmit(mr);
                }
                mr.DateTime = DateTime.Now;
                mr.Role = role.ToString();
                mr.Message = message;

                context.SubmitChanges();
            }

            AuthUser owner;
            using (CandyAuth auth = new CandyAuth())
            {
                owner = auth.GetUserByName(ownerName);

            }


            //EMailManager.SendMail("noreply@candy.com", owner.EMail, "membership request", string.Format("Hi {0}, \r\n {1} would like to join your project {2} as a {3}.  \r\n Thanks Candy", owner.Name, CurrentUserName, project, role.ToString() ));

            EMailManager.SendMail("noreply@candy.com", "jan.hannemann@outlook.com", "membership request", string.Format("Hi {0}, \r\n {1} would like to join your project {2} as a {3}.  \r\n Thanks Candy", owner.Name, CurrentUserName, project, role.ToString()));
        }

        [Invoke(HasSideEffects = true)]
        [RequiresAuthentication]
        public ProjectUserDto GrantMembership(MembershipRequestDto request)
        {
            //SecurityManager.CheckForRoleManagerOrHigher(CurrentUserName, request.ProjectId, "GrantMembership");

            //DataAccessManager dam = new DataAccessManager();

            switch (request.RoleType)
            {
                case RoleType.SU:
                    break;
                case RoleType.GUEST:
                    {
                        CandySecurityManager.AddToGuests(request.User, request.ProjectId);
                        break;
                    }
                case RoleType.MEMBER:
                    {
                        CandySecurityManager.AddToMembers(request.User, request.ProjectId);
                        break;
                    }
                case RoleType.OWNER:
                    {
                        CandySecurityManager.AddToOwners(request.User, request.ProjectId);
                        break;
                    }
                default:
                    break;
            }
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(request.ProjectId)))
            {
                User u = context.Users.SingleOrDefault(x => x.UId == request.UserUId);
                if (u == null)
                {
                    u = new User()
                   {
                       UId = request.UserUId,
                       UserName = request.User
                   };
                    context.Users.InsertOnSubmit(u);
                    context.SubmitChanges();
                }

            }
            ProjectUserDto ret = new ProjectUserDto()
            {
                UserUID = request.UserUId,
                Username = request.User
            };

            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                var r = context.MembershipRequests.SingleOrDefault(x => x.ProjectId == request.ProjectId && x.UserUid == request.UserUId);
                if (r != null)
                {
                    context.MembershipRequests.DeleteOnSubmit(r);
                    context.SubmitChanges();
                }
            }
            var user = Membership.GetUser(request.User);
            EMailManager.SendMail("noreply@candy.com", user.Email, "Access granted", "You have access to your requested project now.");
            return ret;
        }

        [Invoke(HasSideEffects = true)]
        [RequiresAuthentication]
        public void RejectMembership(MembershipRequestDto request)
        {
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                var r = context.MembershipRequests.SingleOrDefault(x => x.ProjectId == request.ProjectId && x.UserUid == request.UserUId);
                if (r != null)
                {
                    context.MembershipRequests.DeleteOnSubmit(r);
                    context.SubmitChanges();
                }
            }
            var user = Membership.GetUser(request.User);
            EMailManager.SendMail("noreply@candy.com", user.Email, "Membership request rejected", string.Format("Your request to join project {0} was rejected.", request.ProjectId));

        }

        [Invoke(HasSideEffects = true)]
        [RequiresAuthentication]
        public void LeaveProject(int projectId)
        {
            //var project = GetProjects().SingleOrDefault(x => x.Id == projectId);
            //if (project == null)
            //    throw new ArgumentException("Unknown project");
            //if (project.Owner == CurrentUserName)
            //    throw new InvalidOperationException("The owner can't leave the project");

            //SecurityManager.RemoveFromGuests(CurrentUserName, projectId);
            //SecurityManager.RemoveFromMembers(CurrentUserName, projectId);
            //SecurityManager.RemoveFromManagers(CurrentUserName, projectId);

        }


        [Query]
        public IQueryable<ProjectUserDto> GetProjectUser(int projectId)
        {
            List<ProjectUserDto> ret;
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                ret = (from x in context.Users
                       select new ProjectUserDto
                       {
                           UserUID = x.UId,
                           Username = x.UserName,
                           ProfilePictureURL = CandySecurityManager.GetUser(x.UserName).PictureURL,
                           ProjectId = projectId,
                           IsOwner = Roles.IsUserInRole(x.UserName, RoleDefinition.FormatOwnerRole(projectId)),
                           IsGuest = Roles.IsUserInRole(x.UserName, RoleDefinition.FormatGuestRole(projectId)),
                           IsMember = Roles.IsUserInRole(x.UserName, RoleDefinition.FormatMemberRole(projectId)),

                       }).ToList();
            }
            return ret.AsQueryable();
        }

        [Update]
        public void UpdateProjectUser(ProjectUserDto projectUser)
        {
            if (projectUser.IsGuest)
                CandySecurityManager.AddToGuests(projectUser.Username, projectUser.ProjectId);
            else
                CandySecurityManager.RemoveFromGuests(projectUser.Username, projectUser.ProjectId);
            if (projectUser.IsMember)
                CandySecurityManager.AddToMembers(projectUser.Username, projectUser.ProjectId);
            else
                CandySecurityManager.RemoveFromMembers(projectUser.Username, projectUser.ProjectId);
            if (projectUser.IsOwner)
                CandySecurityManager.AddToOwners(projectUser.Username, projectUser.ProjectId);
            else
                CandySecurityManager.RemoveFromOwners(projectUser.Username, projectUser.ProjectId);

        }

        [Delete]
        public void DeleteProjectUser(ProjectUserDto projectUser)
        {
            if (projectUser.IsOwner)
            {
                ValidationResult error =
                 new ValidationResult(Resources.ValidationErrorResource.OwnerCanNotBeDeleted);
                // new string[] { "User" });
                throw new ValidationException(error, null, projectUser);
            }

            CandySecurityManager.RemoveFromGuests(projectUser.Username, projectUser.ProjectId);
            CandySecurityManager.RemoveFromMembers(projectUser.Username, projectUser.ProjectId);
            CandySecurityManager.RemoveFromOwners(projectUser.Username, projectUser.ProjectId);
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectUser.ProjectId)))
            {
                var u = context.Users.SingleOrDefault(x => x.UId == projectUser.UserUID);
                if (u != null)
                {
                    context.Users.DeleteOnSubmit(u);
                    context.SubmitChanges();
                }
            }
        }

        /// <summary>
        /// Checks whether a project already exists.
        /// </summary>
        /// <param name="projectId">The id of the project that may already holds the name/</param>
        /// <param name="projectName">The name of the project to check.</param>
        /// <returns></returns>
        [Invoke(HasSideEffects = false)]
        public bool ProjectExists(string projectName)
        {
            using (AuthDatabaseDataContext context = new AuthDatabaseDataContext())
            {
                if (string.IsNullOrEmpty(projectName))
                    return false;
                else
                    return context.Projects.Any(x => x.Name.ToLower() == projectName.ToLower());
            }
        }
        #endregion

        #region FishingDbs

        [Query]
        public IQueryable<DatabaseDto> GetDatabases(int projectId)
        {
            SyncMasterDatabases(projectId);

            List<DatabaseDto> ret = new List<DatabaseDto>();
            string connStr = ProjectConnector.GetCandyConnectionString(projectId);
            using (CandyDbDataContext context = new CandyDbDataContext(connStr))
            {
                var r = context.Databases.Select(x => new
                DatabaseDto
                {
                    Id = x.Id,
                    UId = x.MasterUId.Value,
                    Name = x.Name,
                    Collection = x.Collection,
                    ProjectId = projectId,
                    Version = x.Version,
                    DisplayName = x.DisplayName,
                    IsEnabled = x.FishingEnabled,
                    IsFishingDB = x.IsFishingDB,
                    IsSecondaryDB = x.IsSecondaryDB,
                    LocalDBName = x.LocalDBName
                });
                ret.AddRange(r);
            }


            return ret.AsQueryable();
        }


        [Query]
        public IQueryable<DatabaseDto> GetFishingDbs(int projectId)
        {


            List<DatabaseDto> ret = new List<DatabaseDto>();
            string connStr = ProjectConnector.GetCandyConnectionString(projectId);
            using (CandyDbDataContext context = new CandyDbDataContext(connStr))
            {
                var r = context.Databases.Where(x => x.IsFishingDB).Select(x => new
                DatabaseDto
                {
                    Id = x.Id,
                    UId = x.MasterUId.Value,
                    Name = x.Name,
                    Collection = x.Collection,
                    ProjectId = projectId,
                    Version = x.Version,
                    DisplayName = x.DisplayName,
                    IsEnabled = x.FishingEnabled,
                    IsFishingDB = x.IsFishingDB,
                    IsSecondaryDB = x.IsSecondaryDB
                });
                ret.AddRange(r);
            }


            return ret.AsQueryable();
        }

        private void SyncMasterDatabases(int projectId)
        {
            CandyMasterDbDataContext master = new CandyMasterDbDataContext();
            var masterDbs = master.MasterDatabases.ToList();
            master.Dispose();

            string connStr = ProjectConnector.GetCandyConnectionString(projectId);
            using (CandyDbDataContext context = new CandyDbDataContext(connStr))
            {
                var dbs = context.Databases.Where(x => x.MasterUId.HasValue).ToList();
                //update existing
                foreach (var item in dbs)
                {
                    var mdb = masterDbs.Single(x => x.UId == item.MasterUId);
                    item.MasterUId = mdb.UId;
                    item.Collection = mdb.Collection;
                    item.DisplayName = mdb.DisplayName;
                    item.IsFishingDB = mdb.IsFishingDB;
                    item.IsSecondaryDB = mdb.IsSecondaryDB;
                    item.LocalDBName = mdb.LocalDBName;
                    item.Name = mdb.Name;
                    item.Version = mdb.Version;
                }

                //add new dbs
                var newDBs = masterDbs.Where(x => dbs.Any(y => y.MasterUId == x.UId) == false);
                foreach (var item in newDBs)
                {
                    Database db = new Database()
                    {
                        MasterUId = item.UId,
                        Collection = item.Collection,
                        DisplayName = item.DisplayName,
                        FishingEnabled = false,
                        IsFishingDB = item.IsFishingDB,
                        IsSecondaryDB = item.IsSecondaryDB,
                        LocalDBName = item.LocalDBName,
                        Name = item.Name,
                        Version = item.Version
                    };
                    context.Databases.InsertOnSubmit(db);
                }
                context.SubmitChanges();
            }
        }

        [Update]
        public void UpdateFishingDb(DatabaseDto db)
        {
            string connStr = ProjectConnector.GetCandyConnectionString(db.ProjectId);
            using (CandyDbDataContext context = new CandyDbDataContext(connStr))
            {
                var d = context.Databases.Where(x => x.IsFishingDB && x.MasterUId.HasValue).Single(x => x.MasterUId == db.UId);
                d.FishingEnabled = db.IsEnabled;
                context.SubmitChanges();
            }
        }

        [RequiresAuthentication()]
        [Query]
        public IQueryable<HitResultDto> GetBlastResults(string project, int jobId)
        {
            List<HitResultDto> ret = new List<HitResultDto>();

            using (CandyDbDataContext candyContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(project)))
            {
                BlastJob job = candyContext.BlastJobs.Single(x => x.Id == jobId);
                ret = (from x in candyContext.TopHitsViews
                       where x.BlastJobId == jobId
                       select new HitResultDto
                       {
                           Id = x.Id,
                           BlastJobId = x.BlastJobId,
                           ProposedEntryType = GeneEntryType.homolog,
                           AssignedBaitName = x.AssignedBaitName,
                           AssignedBaitId = x.AssignedBaitId,
                           AssignedGeneFamilyId = x.AssignedGeneFamilyId,
                           AssignedGeneFamilyName = x.AssignedGeneFamilyName,
                           AssignedGeneIdentifiere = x.AssignedGeneIdentifiere,
                           AssignedGeneName = x.AssignedGeneName,
                           AssignedGeneId = x.AssignedGeneId,
                           Description = x.Description,
                           Expectation = x.Expectation,
                           GeneHitCounter = x.GeneHitCounter,
                           GeneId = x.GeneId,
                           HspsCounter = x.HspsCounter,
                           Identity = x.Identity,
                           Length = x.Length,
                           MatchSeq = x.MatchSeq,
                           Pattern = x.Pattern,
                           Positives = x.Positives,
                           Probability = x.Probability,
                           QuerySeq = x.QuerySeq,
                           RequestGeneFamilyName = x.RequestingGeneFamilyName,
                           RequestingGeneFamilyId = x.RequestingGeneFamilyId,
                           RequestingGeneIdentifiere = x.RequestingGeneIdentifiere,
                           RequestingGeneName = x.RequestingGeneName,
                           RequestingGeneSR = x.RequestingGeneSR,
                           RequestingGeneId = x.RequestingGeneId,
                           Score = x.Score,
                           DatabaseId = job.DatabaseId,
                           DatabaseMasterUId = job.Database.MasterUId
                       }).ToList();



            }

            return ret.AsQueryable();
        }

        [Update]
        public void UpdateResultFake(HitResultDto dto)
        {
            //fake update method to allow editing on the client side
        }

        #endregion

        #region JobManagement
        [RequiresAuthentication()]
        [Query]
        public IQueryable<BlastJobDto> GetJobs(int projectId)
        {
            CandyBlast.Blaster blaster = new CandyBlast.Blaster();
            blaster.RecheckOldJobs(projectId);
            List<BlastJobDto> ret = new List<BlastJobDto>();

            using (CandyDbDataContext candyContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                Guid currentUserUId = CurrentUser.UserID;
                foreach (var item in candyContext.BlastJobs.Where(x => x.UserUId == currentUserUId))
                {

                    BlastJobDto dto = new BlastJobDto();
                    dto.BaitId = item.GeneId;
                    dto.FamilyId = item.FamilyId;
                    dto.DateTime = item.TimePoint;
                    dto.Error = item.ErrorMessage;
                    dto.Name = item.Name;
                    dto.Status = (RequestStatus)item.Status;
                    dto.Task = (BlastTask)item.Task;
                    dto.Id = item.Id;
                    dto.UserUId = item.UserUId;
                    dto.ProjectId = projectId;
                    dto.FamilyName = item.FamilyId.HasValue ? candyContext.GeneFamilies.Single(x => x.Id == item.FamilyId.Value).Name : null;
                    dto.BaitName = item.GeneId.HasValue ? candyContext.Genes.Single(x => x.Id == item.GeneId.Value).Name : null;
                    ret.Add(dto);
                }
            }
            return ret.AsQueryable();
        }

        [Query]
        public IQueryable<BlastJobDto> RefreshJobs(Guid currentUserUId, int projectId, IEnumerable<int> filter, DateTime dateMinThreshold)
        {

            CandyBlast.Blaster blaster = new CandyBlast.Blaster();
            blaster.RecheckOldJobs(projectId);
            List<BlastJobDto> ret = new List<BlastJobDto>();
          //  Guid currentUserUId = CurrentUser.UserID;
            using (CandyDbDataContext candyContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {

                foreach (var item in (from j in candyContext.BlastJobs.Where(x => x.UserUId == currentUserUId).ToList()
                                      where filter.Any(x => x == j.Id) || j.TimePoint > dateMinThreshold
                                      select j
                                          ))
                {

                    BlastJobDto dto = new BlastJobDto();
                    dto.BaitId = item.GeneId;
                    dto.FamilyId = item.FamilyId;
                    dto.DateTime = item.TimePoint;
                    dto.Error = item.ErrorMessage;
                    dto.Name = item.Name;
                    dto.Status = (RequestStatus)item.Status;
                    dto.Task = (BlastTask)item.Task;
                    dto.Id = item.Id;
                    dto.UserUId = item.UserUId;
                    dto.ProjectId = projectId;
                    dto.FamilyName = item.FamilyId.HasValue ? candyContext.GeneFamilies.Single(x => x.Id == item.FamilyId.Value).Name : null;
                    dto.BaitName = item.GeneId.HasValue ? candyContext.Genes.Single(x => x.Id == item.GeneId.Value).Name : null;
                    ret.Add(dto);
                }
            }
            return ret.AsQueryable();
        }

        [Invoke]
        public List<BlastJobDto> CreateJob(int projectId, BlastJobDefinitionDto dto)
        {
            List<CandyBlast.BlastJob> bjs = new List<CandyBlast.BlastJob>();
            List<BlastJobDto> ret = new List<BlastJobDto>();
            switch (dto.Task)
            {
                case BlastTask.find_ortholog_genes:
                    {
                        CandyBlast.Blaster blaster = new CandyBlast.Blaster();
                        var user = CurrentUser;
                        bjs.Add(blaster.BlastEbi(projectId, dto.GeneId, dto.SecondaryBaitDatabase, user.UserID, user.EMail));
                        break;
                    }
                case BlastTask.blast_fishing_dbs:
                    {

                        CandyBlast.Blaster blaster = new CandyBlast.Blaster();
                        bjs.AddRange(blaster.BlastFishingDbs(projectId, dto.FamilyId, CurrentUser.UserID));
                        break;
                    }
                default:
                    break;
            }

            foreach (var item in bjs)
            {
                BlastJobDto bjd = new BlastJobDto();
                bjd.BaitId = item.GeneId;
                bjd.FamilyId = item.FamilyId;
                bjd.DateTime = item.TimePoint;
                bjd.Error = item.ErrorMessage;
                bjd.Name = item.Name;
                bjd.Status = (RequestStatus)item.Status;
                bjd.Task = (BlastTask)item.Task;
                bjd.Id = item.Id;
                bjd.UserUId = item.UserUId;
                bjd.ProjectId = projectId;
                ret.Add(bjd);
            }
            return ret;
        }

        [Invoke]
        public List<BlastJobDto> CreateJobs(int projectId, IEnumerable<BlastJobDefinitionDto> dtos)
        {
            List<BlastJobDto> ret = new List<BlastJobDto>();
            foreach (var item in dtos)
            {
                ret.AddRange(CreateJob(projectId, item));
            }
            return ret;
        }

        [Delete]
        public void DeleteJob(BlastJobDto job)
        {
            using (CandyDbDataContext candyContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(job.ProjectId)))
            {
                var j = candyContext.BlastJobs.Single(x => x.Id == job.Id);
                candyContext.BlastJobs.DeleteOnSubmit(j);
                candyContext.SubmitChanges();
            }
        }


        [Invoke]
        public GeneDto ImportCandidateGene(int projectId, HitResultDto dto)
        {


            CandyMasterDbDataContext masterContext = new CandyMasterDbDataContext();
            CandyDbDataContext projectContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId));


            StringBuilder builderDesc = new StringBuilder();
            StringBuilder builderRational = new StringBuilder();
            //Tag famTag = null;
            bool isReimport = true;

            Gene importGene = projectContext.Genes.SingleOrDefault<Gene>(x => x.GeneIdentifiere == dto.GeneId && x.DatabaseId == dto.DatabaseId);
            if (importGene == null)
            {
                var templateGene = masterContext.TemplateGenes.SingleOrDefault<TemplateGene>(x => x.GeneIdentifiere == dto.GeneId && x.Database_Fk == dto.DatabaseMasterUId);
                //well couldn;t find the gene
                if (templateGene == null)
                {
                    throw new ArgumentException("Couldn't find gene for import.");
                }
                isReimport = false;

                Database db = projectContext.Databases.Single(x => x.MasterUId == templateGene.Database_Fk);
                Genotype gt = projectContext.Genotypes.Single(x => x.MasterUId == templateGene.Genotype_Fk);
                importGene = new Gene()
                {
                    //UId = templateGene.UId,
                    GeneIdentifiere = templateGene.GeneIdentifiere,
                    Name = templateGene.Name,
                    ScientificRelevance = 1,
                    PROTSequence = templateGene.PROTSequence,
                    DatabaseId = db.Id,
                    GenotypeId = gt.Id
                };
            }


            // throw new NotImplementedException();
            //TODO: needs to be redone
            Gene bait = projectContext.Genes.SingleOrDefault<Gene>(x => x.Id == dto.RequestingGeneId);

            if (isReimport)
            {

                //    GeneFamily prevFam = projectContext.GeneFamilies.SingleOrDefault(x => x.UId == importGene.Family_Fk);
                //    if (dto.RequestGeneFamilyUId != prevFam.UId)
                //    {
                //        builderDesc.Append("\r\n__________________\r\n");
                //        builderRational.Append("\r\n__________________\r\n");
                //        builderRational.AppendFormat("Gene re-imported as {0} {1} at {2} by {3}.\r\n", new object[] { dto.ProposedEntryType.ToString().ToLower().Replace("_", " ").Replace("bait", ""), bait.GeneIdentifiere, DateTime.Now.ToShortDateString(), CurrentUserName });
                //        builderDesc.AppendFormat("Gene re-imported as {0} {1} at {2} by {3}.\r\n", new object[] { dto.ProposedEntryType.ToString().ToLower().Replace("_", " ").Replace("bait", ""), bait.GeneIdentifiere, DateTime.Now.ToShortDateString(), CurrentUserName });

                //        builderRational.AppendFormat("Bait gene rationale is:\r\n\t{0}", bait.Rational);
                //        builderDesc.AppendFormat("Bait gene description is:\r\n\t{0}", bait.Description);

                //        string famTagName = GetFamPath(prevFam, projectContext).Trim();
                //        famTag = projectContext.Tags.FirstOrDefault(x => x.Name.Trim().ToLower() == famTagName.Trim().ToLower());
                //        if (famTag == null)
                //        {
                //            famTag = new Tag { UId = Guid.NewGuid(), Name = famTagName };
                //            projectContext.Tags.InsertOnSubmit(famTag);
                //            projectContext.assigneds.InsertOnSubmit(new assigned { Gene_Fk = importGene.UId, Tag_Fk = famTag.UId });
                //            projectContext.SubmitChanges();
                //        }
                //    }
            }
            else
            {
                //    if (bait != null)
                //    {
                //        builderRational.AppendFormat("Gene imported as {0} {1} at {2} by {3}.\r\n", new object[] { dto.ProposedEntryType.ToString().ToLower().Replace("_", " ").Replace("bait", ""), bait.GeneIdentifiere, DateTime.Now.ToShortDateString(), CurrentUserName });
                //        builderDesc.AppendFormat("Gene imported as {0} {1} at {2} by {3}.\r\n", new object[] { dto.ProposedEntryType.ToString().ToLower().Replace("_", " ").Replace("bait", ""), bait.GeneIdentifiere, DateTime.Now.ToShortDateString(), CurrentUserName });

                //        builderRational.AppendFormat("Bait gene rationale is:\r\n\t{0}", bait.Rational);
                //        builderDesc.AppendFormat("Bait gene description is:\r\n\t{0}", bait.Description);
                //    }

            }


            importGene.EntryType = dto.ProposedEntryType.ToString();
            //importGene.Description += builderDesc.ToString();
            //importGene.Rational += builderRational.ToString();
            importGene.BaitId = bait.Id;
            importGene.FamilyId = bait.FamilyId;
            importGene.GeneFamily = bait.GeneFamily;

            if (!isReimport)
            {
                importGene.CreatedAt = DateTime.Now;
                importGene.CreatedByUId = CurrentUser.UserID;
                projectContext.Genes.InsertOnSubmit(importGene);
            }

            projectContext.SubmitChanges();
            if (!isReimport)
            {
                importGene.Name = Autonaming.NameGene(importGene.Genotype.Name, importGene.Id);
                projectContext.SubmitChanges();
            }

            return new GeneDto
            {
                Id = importGene.Id,
                GeneIdentifier = importGene.GeneIdentifiere,
                GenotypeId = importGene.GenotypeId,
                ScientificRelevance = importGene.ScientificRelevance,
                Name = importGene.Name,
                AutogeneratedName = IsAutoName(importGene.Name, importGene.Id, importGene.Genotype),
                FamilyId = importGene.FamilyId,
                ProjectId = projectId,
                EntryType = dto.ProposedEntryType,
                ProteinSequence = importGene.PROTSequence,
                CreateDate = importGene.CreatedAt,
                CreatedBy = importGene.CreatedByUId.ToString(), //TODO: Get the actual user
                IsCandidate = importGene.Database != null ? importGene.Database.IsFishingDB : false
            };
        }

        [Invoke]
        public GeneDto ImportSecondaryBait(int projectId, HitResultDto dto)
        {
            //    System.Net.GlobalProxySelection.Select = new System.Net.WebProxy("127.0.0.1", 8888);
            using (CandyDbDataContext candyContext = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                if (candyContext.Genes.Any(x => x.GeneIdentifiere == dto.GeneId))
                {
                    candyContext.Genes.DeleteOnSubmit(candyContext.Genes.Single(x => x.GeneIdentifiere == dto.GeneId));
                    candyContext.SubmitChanges();
                }


                WebClient wc = new WebClient();
                string xml = wc.DownloadString(string.Format("http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/uniprotkb/{0}/uniprotxml", dto.GeneId));
                //                System.IO.File.WriteAllText(@"c:\temp\" + dto.GeneId + ".xml", xml);
                XDocument document = XDocument.Parse(xml, LoadOptions.SetBaseUri);
                XNamespace ns = "http://uniprot.org/uniprot";

                string molecularFunction = null;
                var funcNode = document.Descendants(ns + "comment").SingleOrDefault(x => x.Attributes().Any(y => y.Name == "type" && y.Value == "function"));
                if (funcNode != null)
                {
                    var funcValueNode = funcNode.Descendants(ns + "text").FirstOrDefault();
                    if (funcValueNode != null)
                        molecularFunction = funcValueNode.Value;
                }


                IEnumerable<XElement> source = document.Descendants((ns + "protein"));
                //string str2 = "";
                //if (source.Count<XElement>() > 0)
                //{
                //    str2 = source.Descendants<XElement>(((XName)(ns + "recommendedName"))).Descendants<XElement>(((XName)(ns + "fullName"))).First<XElement>().Value;
                //}
                IEnumerable<XElement> enumerable3 = document.Descendants((XName)(ns + "gene"));
                string geneName = "";
                if (enumerable3.Count<XElement>() > 0)
                {
                    XElement element = enumerable3.Descendants<XElement>(((XName)(ns + "name"))).FirstOrDefault<XElement>(delegate(XElement x)
                    {
                        return x.Attribute("type").Value == "primary";
                    });
                    geneName = (element != null) ? element.Value : "";
                }
                Dictionary<string, string> dictionary = new Dictionary<string, string>();
                dictionary.Add("EBI entry for " + dto.GeneId, string.Format("http://www.ebi.ac.uk/cgi-bin/dbfetch?db=UniProtKB&id={0}", dto.GeneId));
                string str4 = "";
                IEnumerable<XElement> enumerable4 = document.Descendants((XName)(ns + "organism"));
                if (enumerable4.Count<XElement>() > 0)
                {
                    XElement element2 = enumerable4.Descendants<XElement>(((XName)(ns + "name"))).SingleOrDefault<XElement>(delegate(XElement x)
                    {
                        return x.Attribute("type").Value == "scientific";
                    });
                    str4 = (element2 != null) ? element2.Value : "";
                    XElement element3 = enumerable4.Descendants<XElement>(((XName)(ns + "dbReference"))).SingleOrDefault<XElement>(delegate(XElement x)
                    {
                        return x.Attribute("type").Value == "NCBI Taxonomy";
                    });
                    if (element3 != null)
                    {
                        dictionary.Add("NCBI Taxonomy", string.Format("http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id={0}", element3.Attribute("id").Value));
                    }
                }
                IEnumerable<XElement> enumerable5 = document.Descendants((XName)(ns + "reference"));
                foreach (XElement element4 in enumerable5)
                {
                    string key = (element4.Descendants((XName)(ns + "title")).FirstOrDefault<XElement>() != null) ? element4.Descendants((XName)(ns + "title")).FirstOrDefault<XElement>().Value : null;
                    XElement element5 = element4.Descendants((XName)(ns + "dbReference")).SingleOrDefault<XElement>(delegate(XElement x)
                    {
                        return x.Attribute("type").Value == "PubMed";
                    });
                    if ((element5 != null) && (key != null))
                    {
                        dictionary.Add(key, "http://www.ncbi.nlm.nih.gov/pubmed/?term=" + element5.Attribute("id").Value);
                    }
                }
                List<string> list = new List<string>();
                IEnumerable<XElement> enumerable6 = document.Descendants((XName)(ns + "keyword"));
                foreach (XElement element4 in enumerable6)
                {
                    list.Add(element4.Value);
                }
                string sequence = document.Descendants((XName)(ns + "entry")).First<XElement>().Element((XName)(ns + "sequence")).Value;
                Gene entity = candyContext.Genes.SingleOrDefault<Gene>(x => x.GeneIdentifiere == dto.GeneId);
                if (entity != null)
                {
                    candyContext.Genes.DeleteOnSubmit(entity);
                }
                // GeneJobView gjv = candyContext.GeneJobViews.SingleOrDefault<GeneJobView>(x => x.JobResultUid == rrv.UId);
                StringBuilder descriptionBuilder = new StringBuilder();
                //if (!string.IsNullOrEmpty(str2))
                //{
                //    descriptionBuilder.AppendFormat("Protein name: {0}\r\n", str2);
                //}
                descriptionBuilder.AppendFormat("Organism: {0}\r\n", str4);
                if (list.Count > 0)
                {
                    descriptionBuilder.AppendFormat("Keywords:\r\n", enumerable4);
                    foreach (string str7 in list)
                    {
                        descriptionBuilder.AppendFormat("\t\t-{0}\r\n", str7);
                    }
                }
                StringBuilder rationalBuilder = new StringBuilder();
                rationalBuilder.AppendFormat("Gene imported as putative ortholog of {2} from SwissProt at {0} - {3} by {1}.\r\n\r\n", new object[] { DateTime.Now.ToShortDateString(), CurrentUserName, dto.RequestingGeneName, DateTime.Now.ToShortTimeString() });
                Gene bait = candyContext.Genes.SingleOrDefault<Gene>(x => x.Id == dto.RequestingGeneId);
                if (bait != null)
                {
                    //TODO: disabled for now
                    //rationalBuilder.AppendFormat("Bait gene rationale was:\r\n\t\t{0}", bait.Rational);
                }
                string[] specArr = str4.ToLower().Split(" ".ToCharArray());
                Genotype genotype = null;
                if (specArr.Length > 1)
                {
                    string genus = specArr[0].Trim();
                    genus = genus.ToUpper()[0] + genus.Substring(1, genus.Length - 1);
                    string species = specArr[1].Trim();

                    //string variety = null;
                    //for (int i = 2; i < specArr.Length; i++)
                    //{
                    //    variety += specArr[i] + " ";

                    //}
                    //variety = variety != null ? variety.Trim() : variety;
                    //foreach (var x in _candyContext.Genotypes)
                    //{
                    //    bool genus = x.Genus.ToLower().Trim() == specArr[0].ToLower().Trim();
                    //    bool species = x.Species.ToLower().Trim() == specArr[1].ToLower().Trim();
                    //    bool variety = !(x.Variety != null || x.Variety != "");
                    //}
                    genotype = candyContext.Genotypes.ToList().FirstOrDefault<Genotype>(x => x.Genus.ToLower().Trim() == genus.ToLower()
                                                                                        && x.Species.ToLower().Trim() == species.ToLower()
                                                                                        );//&& (x.Variety == variety)
                    //if (genotype == null && variety == null) //lets try again with an empty string
                    //    genotype = candyContext.Genotypes.ToList().SingleOrDefault<Genotype>(x => x.Genus.ToLower().Trim() == genus.ToLower()
                    //                                                                    && x.Species.ToLower().Trim() == species.ToLower()
                    //                                                                    && (x.Variety == ""));
                    if (genotype == null)
                    {
                        string genotypeName;
                        Autonaming.NameGenotype(candyContext.Genotypes.Select(x => x.Name).ToList(), genus, species, out genotypeName);
                        genotype = new Genotype
                        {
                            Genus = genus,
                            Species = species,
                            Name = genotypeName
                        };
                        candyContext.Genotypes.InsertOnSubmit(genotype);
                        candyContext.SubmitChanges();
                    }
                }


                var user = CurrentUser;

                Gene gene = new Gene();
                gene.CreatedAt = DateTime.Now;
                gene.CreatedByUId = user.UserID;
                gene.EntryType = dto.ProposedEntryType.ToString();
                gene.PROTSequence = sequence.Replace("\r", "").Replace("\n", "").Replace(" ", "").Replace("*", "");
                gene.DatabaseId = dto.DatabaseId;
                gene.GenotypeId = (genotype != null) ? genotype.Id : default(int?);
                gene.GeneIdentifiere = dto.GeneId;
                gene.Name = dto.GeneId;
                //gene.Description = descriptionBuilder.ToString();
                //gene.Rational = rationalBuilder.ToString();
                gene.ScientificRelevance = 1;
                gene.BaitId = dto.RequestingGeneId;
                gene.FamilyId = dto.RequestingGeneFamilyId;
                candyContext.Genes.InsertOnSubmit(gene);
                candyContext.SubmitChanges();
                if (genotype != null)
                    gene.Name = Autonaming.NameGene(genotype.Name, gene.Id);
                else
                    gene.Name = Autonaming.NameGene(null, gene.Id);


                foreach (KeyValuePair<string, string> pair in dictionary)
                {
                    AnnotationValue reference = new AnnotationValue();
                    reference.EntityId = gene.Id;
                    reference.EntityType = EntityType.Gene.ToString();
                    reference.CreateDate = DateTime.Now;
                    reference.ModifiedDate = reference.CreateDate;
                    reference.CreatorUserUId = user.UserID;
                    reference.ModifierUserUId = user.UserID;
                    reference.AnnotationType = AnnotationType.Reference.ToString();
                    reference.Value = string.Format("{0}\t{1}", pair.Key, pair.Value);
                    candyContext.AnnotationValues.InsertOnSubmit(reference);
                }

                if (descriptionBuilder.Length > 0)
                {
                    AnnotationValue description = new AnnotationValue();
                    description.EntityId = gene.Id;
                    description.EntityType = EntityType.Gene.ToString();
                    description.CreateDate = DateTime.Now;
                    description.ModifiedDate = description.CreateDate;
                    description.CreatorUserUId = user.UserID;
                    description.ModifierUserUId = user.UserID;
                    description.AnnotationType = AnnotationType.Description.ToString();
                    description.Value = descriptionBuilder.ToString();
                    candyContext.AnnotationValues.InsertOnSubmit(description);
                }

                if (!string.IsNullOrEmpty(molecularFunction))
                {
                    AnnotationValue molFunc = new AnnotationValue();
                    molFunc.EntityId = gene.Id;
                    molFunc.EntityType = EntityType.Gene.ToString();
                    molFunc.CreateDate = DateTime.Now;
                    molFunc.ModifiedDate = molFunc.CreateDate;
                    molFunc.CreatorUserUId = user.UserID;
                    molFunc.ModifierUserUId = user.UserID;
                    molFunc.AnnotationType = AnnotationType.MolecularFunction.ToString();
                    molFunc.Value = molecularFunction;
                    candyContext.AnnotationValues.InsertOnSubmit(molFunc);
                }

                candyContext.SubmitChanges();
                return new GeneDto
                {
                    Id = gene.Id,
                    GeneIdentifier = gene.GeneIdentifiere,

                    FamilyId = gene.FamilyId,
                    GenotypeId = gene.GenotypeId,
                    IsCandidate = false,
                    ProjectId = projectId,
                    ProteinSequence = gene.PROTSequence,
                    ScientificRelevance = gene.ScientificRelevance,
                    CreatedBy = CurrentUser.Name,
                    CreateDate = gene.CreatedAt,
                    AutogeneratedName = true,
                    Name = gene.Name,

                    EntryType = (GeneEntryType)Enum.Parse(typeof(GeneEntryType), gene.EntryType)
                };
            }
        }
        private string GetFamPath(GeneFamily item, CandyDbDataContext candyContext)
        {
            if (item.ParentFamilyId == null)
                return item.Name;
            else
                return GetFamPath(candyContext.GeneFamilies.Single(x => x.Id == item.ParentFamilyId), candyContext) + "/" + item.Name;
        }
        #endregion


        [Query]
        public IQueryable<FamilyDto> GetFamilies(int projectId, bool includeGenes)
        {
            List<FamilyDto> ret = null;
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                ret = (from x in context.FamilyViews.ToList()
                       select new FamilyDto
                       {
                           Id = x.Id,
                           CreateDate = x.CreatedAt,
                           CreatedBy = x.CreatedBy,
                           Name = x.Name,
                           ParentId = x.ParentFamilyId,
                           ProjectId = projectId,
                           Type = (FamilyType)Enum.Parse(typeof(FamilyType), x.Type),
                           Items = context.Genes.Where(g => g.FamilyId == x.Id).Select(gd =>
                               new GeneDto
                               {
                                   Id = gd.Id,
                                   GeneIdentifier = gd.GeneIdentifiere,
                                   GenotypeId = gd.GenotypeId,
                                   ScientificRelevance = gd.ScientificRelevance,
                                   Name = gd.Name,
                                   AutogeneratedName = IsAutoName(gd.Name, gd.Id, gd.Genotype),
                                   FamilyId = gd.FamilyId,
                                   ProjectId = projectId,
                                   EntryType = (GeneEntryType)Enum.Parse(typeof(GeneEntryType), gd.EntryType),
                                   ProteinSequence = gd.PROTSequence,
                                   CreateDate = gd.CreatedAt,
                                   CreatedBy = context.Users.Single(y => y.UId == gd.CreatedByUId).UserName,
                                   IsCandidate = gd.Database != null ? gd.Database.IsFishingDB : false
                               }

                           ).ToList()
                       }).ToList();

            }
            System.IO.File.WriteAllText(@"c:\candylog\getfams.txt", "project: " + projectId + " fams: " + ret.Count());
            return ret.AsQueryable();
            //    return null;
        }

        private bool IsAutoName(string geneName, int geneId, Genotype genotype)
        {
            string autoName = genotype != null ? Autonaming.NameGene(genotype.Name, geneId) : Autonaming.NameGene(null, geneId);
            return geneName.ToLower() == autoName.ToLower();
        }

        [Insert]
        public void InsertFamily(FamilyDto dto)
        {
            if (FamilyExists(dto.ProjectId, dto.Id, dto.Name))
            {
                ValidationResult error = new ValidationResult(Resources.ValidationErrorResource.FamilyNameAlreadyExists,
                            new string[] { "Name" });
                throw new ValidationException(error, null, dto);
            }

            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                GeneFamily gf = new GeneFamily();
                gf.Name = dto.Name;
                gf.ParentFamilyId = dto.ParentId;
                gf.CreatedAt = DateTime.Now;
                gf.CreatedByUId = CurrentUser.UserID;
                gf.Type = dto.Type.ToString();
                context.GeneFamilies.InsertOnSubmit(gf);
                context.SubmitChanges();
                dto.Id = gf.Id;
            }
        }

        [Update]
        public void UpdateFamily(FamilyDto dto)
        {
            if (FamilyExists(dto.ProjectId, dto.Id, dto.Name))
            {
                ValidationResult error = new ValidationResult(Resources.ValidationErrorResource.FamilyNameAlreadyExists,
                            new string[] { "Name" });
                throw new ValidationException(error, null, dto);
            }

            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                GeneFamily gf = context.GeneFamilies.Single(x => x.Id == dto.Id);
                gf.Name = dto.Name;
                gf.ParentFamilyId = dto.ParentId;
                gf.Type = dto.Type.ToString();
                context.SubmitChanges();
            }
        }

        [Invoke(HasSideEffects = false)]
        public bool FamilyExists(int projectId, int currentFamilyId, string familyeName)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                return context.GeneFamilies.ToList().Any(x => x.Id != currentFamilyId && x.Name.Trim().ToLower() == familyeName.Trim().ToLower());
            }
        }

        [Delete]
        public void DeleteFamily(FamilyDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                GeneFamily gf = context.GeneFamilies.Single(x => x.Id == dto.Id);
                DeleteFamilyRecursive(context, gf);

                context.SubmitChanges();
            }
        }

        private void DeleteFamilyRecursive(CandyDbDataContext dataContext, GeneFamily family)
        {
            var childs = dataContext.GeneFamilies.Where(x => x.ParentFamilyId == family.Id);
            foreach (var child in childs)
            {
                DeleteFamilyRecursive(dataContext, child);
            }
            dataContext.GeneFamilies.DeleteOnSubmit(family);

        }

        [Query]
        public IQueryable<GeneDto> GetGenes(int projectId)
        {
            List<GeneDto> ret = null;
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                ret = (from x in context.Genes
                       select new GeneDto
                       {
                           Id = x.Id,
                           GeneIdentifier = x.GeneIdentifiere,
                           Name = x.Name,
                           FamilyId = x.FamilyId,
                           ProjectId = projectId,
                           EntryType = (GeneEntryType)Enum.Parse(typeof(GeneEntryType), x.EntryType),
                           ScientificRelevance = x.ScientificRelevance,
                           ProteinSequence = x.PROTSequence
                       }).ToList();

            }
            return ret.AsQueryable();
        }

        [Insert]
        public void InsertGene(GeneDto dto)
        {

            if (GeneIdentifierExists(dto.ProjectId, dto.Id, dto.GeneIdentifier))
            {
                ValidationResult error = new ValidationResult(Resources.ValidationErrorResource.GeneIdentifierAlreadyExists,
                            new string[] { "GeneIdentifier" });
                throw new ValidationException(error, null, dto);
            }

            if (!dto.AutogeneratedName)
            {
                if (GeneNameExists(dto.ProjectId, dto.Id, dto.Name))
                {
                    ValidationResult error = new ValidationResult(Resources.ValidationErrorResource.GeneNameAlreadyExists,
                                new string[] { "Name" });
                    throw new ValidationException(error, null, dto);
                }
            }
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {

                Gene g = new Gene
                {
                    GeneIdentifiere = dto.GeneIdentifier,
                    Name = dto.AutogeneratedName ? Guid.NewGuid().ToString() : dto.Name,
                    FamilyId = dto.FamilyId,
                    GenotypeId = dto.GenotypeId,
                    EntryType = dto.EntryType.ToString(),
                    PROTSequence = dto.ProteinSequence,
                    CreatedAt = DateTime.Now,
                    CreatedByUId = CurrentUser.UserID
                };
                context.Genes.InsertOnSubmit(g);
                context.SubmitChanges();
                if (dto.AutogeneratedName)
                {
                    if (g.GenotypeId.HasValue)
                    {
                        Genotype gt = context.Genotypes.Single(x => x.Id == g.GenotypeId);
                        g.Name = Autonaming.NameGene(gt.Name, g.Id);
                    }
                    else
                    {
                        g.Name = Autonaming.NameGene(null, g.Id);
                    }
                    context.SubmitChanges();
                    dto.Name = g.Name;
                }

                context.SubmitChanges();
                dto.Id = g.Id;

            }

        }


        [Invoke(HasSideEffects = false)]
        public bool GeneIdentifierExists(int projectId, int currentGeneId, string geneIdentifier)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                var ids = context.Genes.Where(x => x.Id != currentGeneId).Select(x => x.GeneIdentifiere).ToList();
                return ids.Any(x => x.Trim().ToLower() == geneIdentifier.Trim().ToLower());
            }
        }

        [Invoke(HasSideEffects = false)]
        public bool GeneNameExists(int projectId, int currentGeneId, string geneName)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                var names = context.Genes.Where(x => x.Id != currentGeneId).Select(x => x.Name).ToList();
                return names.Any(x => x.Trim().ToLower() == geneName.Trim().ToLower());
            }
        }

        [Update]
        public void UpdateGene(GeneDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                Gene g = context.Genes.Single(x => x.Id == dto.Id);
                g.GeneIdentifiere = dto.GeneIdentifier;
                g.Name = dto.Name;
                g.FamilyId = dto.FamilyId;
                g.GenotypeId = dto.GenotypeId;
                g.PROTSequence = dto.ProteinSequence;
                if (dto.AutogeneratedName)
                {

                    if (g.GenotypeId.HasValue)
                    {
                        Genotype gt = context.Genotypes.Single(x => x.Id == g.GenotypeId);
                        g.Name = Autonaming.NameGene(gt.Name, g.Id);
                    }
                    else
                    {
                        g.Name = Autonaming.NameGene(null, g.Id);
                    }
                    context.SubmitChanges();
                    dto.Name = g.Name;
                }
                else
                {
                    context.SubmitChanges();
                }


            }

        }

        [Delete]
        public void DeleteGene(GeneDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                Gene g = context.Genes.Single(x => x.Id == dto.Id);
                context.Genes.DeleteOnSubmit(g);
                context.SubmitChanges();
            }

        }

        #region Phylogeny
        [Query]
        public IQueryable<PhylogenyDto> GetPhylogenies(int projectId, int familyId)
        {

            List<PhylogenyDto> ret = new List<PhylogenyDto>();
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                FileManager fm = new FileManager();
                ret = (from x in context.Phylogenies
                       where x.FamilyId == familyId
                       select new PhylogenyDto
                       {
                           ProjectId = projectId,
                           Id = x.Id,
                           FamilyId = x.FamilyId,
                           CreateDate = x.CreatedAt,
                           CreatedBy = context.Users.Single(y => y.UId == x.CreatedByUId).UserName,
                           Alignment = x.Alignment,
                           Description = x.Description,
                           Name = x.Name,
                           Tree = x.Tree,
                           CreatorProfilePic = fm.GetProfilePictureUrl(context.Users.Single(y => y.UId == x.CreatedByUId).UserName)
                       }).ToList();

            }

            return ret.AsQueryable();
        }

        [Insert]
        public void InsertPhylogeny(PhylogenyDto dto)
        {
            var user = CurrentUser;
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {

                Phylogeny p = new Phylogeny()
                {
                    Alignment = dto.Alignment,
                    CreatedAt = DateTime.Now,
                    CreatedByUId = user.UserID,
                    Description = dto.Description,
                    FamilyId = dto.FamilyId,
                    Name = dto.Name,
                    Tree = dto.Tree
                };
                context.Phylogenies.InsertOnSubmit(p);
                context.SubmitChanges();
                dto.Id = p.Id;
                dto.CreateDate = p.CreatedAt;
                dto.CreatedBy = user.Name;
            }
        }

        [Update]
        public void UpdatePhylogeny(PhylogenyDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                Phylogeny p = context.Phylogenies.Single(x => x.Id == dto.Id);
                p.Alignment = dto.Alignment;
                p.Description = dto.Description;
                p.FamilyId = dto.FamilyId;
                p.Name = dto.Name;
                p.Tree = dto.Tree;
                context.SubmitChanges();
            }
        }

        [Delete]
        public void DeletePhylogeny(PhylogenyDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                Phylogeny p = context.Phylogenies.Single(x => x.Id == dto.Id);
                context.Phylogenies.DeleteOnSubmit(p);
                context.SubmitChanges();
            }
        }
        #endregion

        #region Annotations
        [Query]
        public IQueryable<GeneAnnotationDto> GetAnnotations(int projectId, int GeneId)
        {
            FileManager fm = new FileManager();
            List<GeneAnnotationDto> ret = new List<GeneAnnotationDto>();
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                ret = (from x in context.AnnotationViews
                       where x.EntityType == EntityType.Gene.ToString()
                       where x.EntityId == GeneId
                       select new GeneAnnotationDto
                       {
                           Id = x.Id,
                           Annotation = (AnnotationType)Enum.Parse(typeof(AnnotationType), x.AnnotationType),
                           GeneId = x.EntityId,
                           ProjectId = projectId,
                           Value = x.Value,
                           CreateDate = x.CreateDate,
                           Creator = x.CreatorUserName,
                           ModifiedDate = x.ModifiedDate,
                           Modifier = x.ModifierUserName,
                           CreatorProfilePic = fm.GetProfilePictureUrl(x.CreatorUserName),
                           ModifierProfilePic = fm.GetProfilePictureUrl(x.ModifierUserName)
                       }).ToList();

            }

            return ret.AsQueryable();
        }

        [Insert]
        public void InsertGeneAnnotation(GeneAnnotationDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                var ann = context.Annotations.SingleOrDefault(x => x.Type == dto.Annotation.ToString());
                if (ann == null)
                {
                    ann = new Annotation()
                    {
                        Type = dto.Annotation.ToString()
                    };
                    context.Annotations.InsertOnSubmit(ann);
                    context.SubmitChanges();
                }

                var user = CurrentUser;
                AnnotationValue av = new AnnotationValue();
                av.AnnotationType = ann.Type;
                av.CreateDate = DateTime.Now;
                av.CreatorUserUId = user.UserID;
                av.ModifiedDate = av.CreateDate;
                av.ModifierUserUId = user.UserID;
                av.EntityId = dto.GeneId;
                av.EntityType = EntityType.Gene.ToString();
                av.Value = dto.Value;
                context.AnnotationValues.InsertOnSubmit(av);
                context.SubmitChanges();
                dto.Id = av.Id;
                dto.CreateDate = av.CreateDate;
                dto.ModifiedDate = av.ModifiedDate;
                dto.Creator = user.Name;
                dto.Modifier = user.Name;

            }
        }

        [Update]
        public void UpdateGeneAnnotation(GeneAnnotationDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                var user = CurrentUser;
                AnnotationValue av = context.AnnotationValues.Single(x => x.Id == dto.Id);
                av.ModifiedDate = DateTime.Now;
                av.ModifierUserUId = user.UserID;
                av.Value = dto.Value;
                context.SubmitChanges();
                dto.ModifiedDate = av.ModifiedDate;
                dto.Modifier = user.Name;
            }
        }

        [Delete]
        public void DeleteGeneAnnotation(GeneAnnotationDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                AnnotationValue av = context.AnnotationValues.Single(x => x.Id == dto.Id);
                context.AnnotationValues.DeleteOnSubmit(av);
                context.SubmitChanges(); ;
            }
        }
        #endregion

        #region Genotypes
        [Query]
        public IQueryable<GenotypeDto> GetGenotypes(int projectId)
        {
            SyncMasterGenotypes(projectId);
            List<GenotypeDto> ret = null;
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                ret = (from x in context.Genotypes
                       select new GenotypeDto
                       {
                           Id = x.Id,
                           Genus = x.Genus,
                           Species = x.Species,
                           Variety = x.Variety,
                           Name = x.Name

                       }).ToList();

            }
            return ret.AsQueryable();
        }

        private void SyncMasterGenotypes(int projectId)
        {
            CandyMasterDbDataContext master = new CandyMasterDbDataContext();
            var masterDbs = master.MasterGenotypes.ToList();
            master.Dispose();

            string connStr = ProjectConnector.GetCandyConnectionString(projectId);
            using (CandyDbDataContext context = new CandyDbDataContext(connStr))
            {
                var dbs = context.Genotypes.Where(x => x.MasterUId.HasValue).ToList();
                ////update existing  //makes problems with existing projects
                //foreach (var item in dbs)
                //{
                //    var mdb = masterDbs.Single(x => x.UId == item.MasterUId);
                //    item.MasterUId = mdb.UId;
                //    item.Genus = mdb.Genus;
                //    item.Species = mdb.Species;
                //    item.Variety = mdb.Variety;
                //    item.Name = mdb.Name;

                //}

                //add new dbs
                var newDBs = masterDbs.Where(x => dbs.Any(y => y.MasterUId == x.UId) == false);
                foreach (var item in newDBs)
                {
                    Genotype gt = context.Genotypes.FirstOrDefault(x => x.Genus == item.Genus && x.Species == item.Species);
                    if (gt == null)
                    {
                        gt = new Genotype()
                        {
                            MasterUId = item.UId,
                            Genus = item.Genus,
                            Species = item.Species

                        };
                        var names = context.Genotypes.Select(x => x.Name).ToList();
                        string name;
                        if (Autonaming.NameGenotype(names, gt.Genus, gt.Species, out name) == true)
                        {
                            gt.Name = name;
                            context.Genotypes.InsertOnSubmit(gt);

                        }
                    }
                    else
                    {
                        gt.MasterUId = item.UId;
                       
                    }
                }
                context.SubmitChanges();
            }
        }

        [Insert]
        public void InsertGenotype(GenotypeDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {

                var names = context.Genotypes.Select(x => x.Name).ToList();
                string name;
                if (Autonaming.NameGenotype(names, dto.Genus, dto.Species, out name) == false)
                {
                    throw new InvalidOperationException("Couldn't find a unique name for genotype.");
                }
                Genotype g = new Genotype
                {
                    Genus = dto.Genus,
                    Species = dto.Species,
                    Variety = dto.Variety,
                    Name = name
                };
                context.Genotypes.InsertOnSubmit(g);
                context.SubmitChanges();
                dto.Id = g.Id;
                dto.Name = name;

            }

        }


        [Invoke(HasSideEffects = false)]
        public bool GenotypeExists(int projectId, GenotypeDto genotype)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(projectId)))
            {
                return context.Genotypes.Any(x => x.Genus == genotype.Genus && x.Species == genotype.Species && x.Variety == genotype.Variety);

            }
        }

        [Update]
        public void UpdateGenotype(GenotypeDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {

                var names = context.Genotypes.Select(x => x.Name).ToList();
                string name;
                if (Autonaming.NameGenotype(names, dto.Genus, dto.Species, out name) == false)
                {
                    throw new InvalidOperationException("Couldn't find a unique name for genotype.");
                }
                Genotype g = context.Genotypes.Single(x => x.Id == dto.Id);
                g.Genus = dto.Genus;
                g.Species = dto.Species;
                g.Variety = dto.Variety;
                g.Name = name;
                context.SubmitChanges();
                dto.Name = name;

            }

        }

        [Delete]
        public void DeleteGenotype(GenotypeDto dto)
        {
            using (CandyDbDataContext context = new CandyDbDataContext(ProjectConnector.GetCandyConnectionString(dto.ProjectId)))
            {
                Genotype g = context.Genotypes.Single(x => x.Id == dto.Id);
                context.Genotypes.DeleteOnSubmit(g);
                context.SubmitChanges();
            }

        }
        #endregion

        protected override void OnError(DomainServiceErrorInfo errorInfo)
        {
            base.OnError(errorInfo);
        }

    }
}